package com.fengwk.user.face.aspect;

import java.lang.annotation.Annotation;
import java.lang.reflect.Parameter;
import java.util.Arrays;

import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;

import com.fengwk.support.exception.BaseException;
import com.fengwk.support.util.ArrayUtils;
import com.fengwk.support.util.AspectjUtils;
import com.fengwk.support.util.ObjectUtils;
import com.fengwk.support.util.StringUtils;
import com.fengwk.support.util.TypeUtils;
import com.fengwk.support.util.WebUtils;
import com.fengwk.user.face.annotation.Permission;
import com.fengwk.user.face.annotation.Role;
import com.fengwk.user.face.annotation.UserId;
import com.fengwk.user.face.client.LoginClient;
import com.fengwk.user.face.client.ValidationClient;
import com.fengwk.user.face.constant.UserServerConstant;
import com.fengwk.user.face.model.LoginSession;
import com.fengwk.user.face.model.User;

/**
 * 用户服务注解切面
 * 
 * @author fengwk
 * 
 */
@Aspect
public class UserServerAspect {

	private static final ThreadLocal<LoginSession> THREAD_LOCAL_LOGIN_SESSION = new ThreadLocal<>();

	@Autowired
	private HttpServletRequest request;

	@Autowired
	private LoginClient loginClient;
	@Autowired
	private ValidationClient validationClient;

	/**
	 * Role前置通知
	 * 
	 * @param joinPoint
	 * @throws BaseException
	 */
	@Before("@annotation(com.fengwk.user.face.annotation.Role) || @within(com.fengwk.user.face.annotation.Role)")
	public void role(JoinPoint joinPoint) {
		validationAnnotation(joinPoint, Role.class);
	}

	/**
	 * Permission前置通知
	 * 
	 * @param joinPoint
	 * @throws BaseException
	 */
	@Before("@annotation(com.fengwk.user.face.annotation.Permission) || @within(com.fengwk.user.face.annotation.Permission)")
	public void permission(JoinPoint joinPoint) {
		validationAnnotation(joinPoint, Permission.class);
	}

	/**
	 * Logined
	 * 
	 * @param proceedingJoinPoint
	 * @return
	 * @throws Throwable
	 */
	@Around("@annotation(com.fengwk.user.face.annotation.Logined) || @within(com.fengwk.user.face.annotation.Logined)")
	public Object logined(ProceedingJoinPoint pjp) throws Throwable {
		LoginSession loginSession = getLoginSession();
		if (loginSession == null) {
			throwBaseException("获取登录会话失败");
		}
		Signature signature = pjp.getSignature();
		MethodSignature methodSignature = (MethodSignature) signature;
		Parameter[] params = methodSignature.getMethod().getParameters();
		Integer loginSessionIndex = null;
		Integer userIdIndex = null;
		if (ArrayUtils.isNotBlank(params)) {
			for (int i = 0; i < params.length; i++) {
				Parameter param = params[i];
				com.fengwk.user.face.annotation.LoginSession loginSessionA = param
						.getAnnotation(com.fengwk.user.face.annotation.LoginSession.class);
				if (loginSessionA != null) {
					loginSessionIndex = i;
				}
				UserId userId = param.getAnnotation(UserId.class);
				if (userId != null) {
					userIdIndex = i;
				}
			}
		}
		Object[] objs = pjp.getArgs();
		if (loginSessionIndex != null) {
			objs[loginSessionIndex] = loginSession;
		}
		if (userIdIndex != null) {
			User user = loginSession.getUser();
			Long userId = user.getUserId();
			Object indexObj = objs[userIdIndex];
			if (indexObj != null) {
				if (!ObjectUtils.equals(indexObj, userId)) {
					throw new BaseException("UserId不匹配");
				}
			}
			objs[userIdIndex] = user.getUserId();
		}
		Object obj = null;
		try {
			obj = pjp.proceed(objs);
		} catch (Throwable e) {
			throw e;
		} finally {
			clearLoginSession();
		}
		return obj;
	}

	@After("@annotation(com.fengwk.user.face.annotation.Role) "
			+ "|| @within(com.fengwk.user.face.annotation.Role) "
			+ "|| @annotation(com.fengwk.user.face.annotation.Permission) "
			+ "|| @within(com.fengwk.user.face.annotation.Permission) "
			+ "|| @annotation(com.fengwk.user.face.annotation.Logined) "
			+ "|| @within(com.fengwk.user.face.annotation.Logined)")
	public void after() {
		clearLoginSession();
	}

	private <A extends Annotation> void validationAnnotation(JoinPoint joinPoint, Class<A> annotationClass)
			throws BaseException {
		A annotation = AspectjUtils.getAnnotationByJoinPoint(joinPoint, annotationClass);
		LoginSession loginSession = getLoginSession();
		if (loginSession == null) {
			throw new BaseException("获取登录会话失败");
		}
		User user = loginSession.getUser();
		// Role
		if (TypeUtils.isType(annotationClass, Role.class)) {
			Role role = (Role) annotation;
			String[] values = role.value();
			if (ArrayUtils.isNotBlank(values)
					&& !validationClient.validationRoles(user.getUserId(), Arrays.asList(values))) {
				throwBaseException("角色缺失:" + ArrayUtils.join(values, ","));
			}
		}
		// Permission
		if (TypeUtils.isType(annotationClass, Permission.class)) {
			Permission permission = (Permission) annotation;
			String[] values = permission.value();
			if (ArrayUtils.isNotBlank(values)
					&& !validationClient.validationPermissions(user.getUserId(), Arrays.asList(values))) {
				throwBaseException("权限缺失:" + ArrayUtils.join(values, ","));
			}
		}
	}

	private void throwBaseException(String msg) {
		clearLoginSession();
		throw new BaseException(msg);
	}

	private LoginSession getLoginSession() {
		LoginSession loginSession = THREAD_LOCAL_LOGIN_SESSION.get();
		if (loginSession == null) {
			loginSession = loginClient.getLoginSessionByChannelAndToken(
					getParam(UserServerConstant.CHANNEL_KEY), getParam(UserServerConstant.TOKEN_KEY));
			THREAD_LOCAL_LOGIN_SESSION.set(loginSession);
		}
		return loginSession;
	}

	private void clearLoginSession() {
		THREAD_LOCAL_LOGIN_SESSION.remove();
	}

	private String getParam(String param) {
		// 从Parameter获取
		String token = request.getParameter(param);
		// 从头部信息获取
		if (StringUtils.isBlank(token)) {
			token = request.getHeader(param);
		}
		// 从cookie中获取
		if (StringUtils.isBlank(token)) {
			token = WebUtils.getCookie(request, param);
		}
		return token;
	}

}
